// -*-mode:c++; coding:utf-8-*-

#ifndef _XBASE_COMPRESSOR_QUICKLZ_HPP_
#define _XBASE_COMPRESSOR_QUICKLZ_HPP_

#include <unistd.h>		// for ssize_t

namespace {
#include "quicklz/quicklz.h"

#ifdef STREAMING_MODE
#   error STREAMING_MODE flag cannot be used in here
#endif
}

#include <boost/scoped_array.hpp>
#include <boost/asio/buffer.hpp>

#include "stack_or_heap_array.hpp"

namespace xbase
{
	namespace compressor
	{
		class quicklz
		{
		public:
			// return - zlen if success, or nagetive number
			template<typename Buffer>
			static inline
			ssize_t compress(Buffer &buffer,
					      const boost::asio::const_buffer &data) {
				const std::size_t max_out_size = buffer_size(data) + 400;
				const std::size_t workmem_size = SCRATCH_COMPRESS;
				boost::asio::mutable_buffer out = buffer.prepare(max_out_size + workmem_size);

				char *workmem = boost::asio::buffer_cast<char*>(out) + max_out_size;

				size_t real_out_size = qlz_compress(boost::asio::buffer_cast<const char*>(data),
								    boost::asio::buffer_cast<char*>(out),
								    boost::asio::buffer_size(data),
								    workmem);
				buffer.commit(real_out_size);
				return real_out_size;
			}

			// return - zlen if success, or nagetive number
			template<typename Buffer>
			static inline
			ssize_t decompress(Buffer &buffer,
						const boost::asio::const_buffer &data) {
				if(boost::asio::buffer_size(data) < 9)
					return -1;

				const std::size_t expect_out_size = qlz_size_decompressed(boost::asio::buffer_cast<const char *>(data));
				const std::size_t workmem_size = SCRATCH_DECOMPRESS;
				boost::asio::mutable_buffer out = buffer.prepare(expect_out_size + workmem_size);

				char *workmem = boost::asio::buffer_cast<char*>(out) + expect_out_size;

				size_t real_out_size = qlz_decompress(boost::asio::buffer_cast<const char*>(data),
								      boost::asio::buffer_cast<char*>(out),
								      workmem);
				buffer.commit(real_out_size);
				return real_out_size;
			}
		};

		// return - zlen if success, or nagetive number
		//          -1 for input data error;
		//          -2 for output buffer too small
		template<>
		inline
		ssize_t quicklz::decompress(const boost::asio::mutable_buffer &buffer,
						 const boost::asio::const_buffer &data)
		{
			if(boost::asio::buffer_size(data) < 9)
				return -1;

			const std::size_t expect_out_size = qlz_size_decompressed(boost::asio::buffer_cast<const char *>(data));
			if(boost::asio::buffer_size(buffer) < expect_out_size)
				return -2;

			stack_or_heap_array<SCRATCH_DECOMPRESS, 1024 * 1024> workmem_buf;
			char *workmem = workmem_buf.get();

			std::size_t real_out_size = qlz_decompress(boost::asio::buffer_cast<const char*>(data),
								   boost::asio::buffer_cast<char*>(buffer),
								   workmem);
			return real_out_size;
		}

	} // namespace compressor

} // namespace xbase

#endif	// _XBASE_COMPRESSOR_QUICK_LZ_HPP_

